// Copyright 2019 The rhyme_lph Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// q [author] rhyme_lph
// J [email]  rhymelph@gmail.com
// F [github] https://github.com/rhymelph

import 'dart:async';

import 'package:base_plugin/base_plugin.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/src/common/config.dart';
import 'package:finance_app/src/entities/address_entity.dart';
import 'package:finance_app/src/entities/business_contact_entity.dart';
import 'package:finance_app/src/entities/ocr_attachment_entity.dart';
import 'package:finance_app/src/entities/purchase_order_detail_entity.dart';
import 'package:finance_app/src/entities/purchase_order_entity.dart';
import 'package:finance_app/src/entities/quotation_detail_entity.dart';
import 'package:finance_app/src/entities/setting_data_entity.dart';
import 'package:finance_app/src/entities/transaction_entity.dart';
import 'package:finance_app/src/http_service/http_common_service.dart';
import 'package:finance_app/src/http_service/http_purchase_order_service.dart';
import 'package:finance_app/src/mixin/check_plan_mixin.dart';
import 'package:finance_app/src/mixin/form_products_or_services_mixin.dart';
import 'package:finance_app/src/mixin/from_attach_mixin.dart';
import 'package:finance_app/src/mixin/init_edit_data_mixin.dart';
import 'package:finance_app/src/provider/dashboard/purchase/purchases_provider.dart';
import 'package:finance_app/src/provider/dashboard/purchase/views/purchases_bill_provider.dart';
import 'package:finance_app/src/provider/dashboard/purchase/views/purchases_po_provider.dart';
import 'package:finance_app/src/provider/dashboard/purchase/views/purchases_summary_provider.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/utils/date_time_utils.dart';
import 'package:finance_app/src/widgets/bottom_sheet/address_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/calendar_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/currency_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/delete_info_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/got_it_tip_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/judge_bottom_sheet.dart';
import 'package:flutter/material.dart';

class NewPurchaseOrderProvider extends BaseProvider<PurchaseOrderDetailEntity>
    with
        FormAttachMixin<PurchaseOrderDetailEntity>,
        FormProductsOrServicesMixin<PurchaseOrderDetailEntity>,
        InitEditDataMixin<PurchaseOrderDetailEntity>,
        CheckPlanMixin {
  bool isSelectMode = false;

  TextEditingController poNumberController = TextEditingController();
  FocusNode poNumberFocusNode = FocusNode();

  TextEditingController unitNumberController = TextEditingController();
  FocusNode unitNumberFocusNode = FocusNode();

  TextEditingController postalCodeController = TextEditingController();
  FocusNode postalCodeFocusNode = FocusNode();

  DateTime deliveryDate;

  SettingDataCurrency currency;

  TextEditingController notesController = TextEditingController();
  FocusNode notesFocusNode = FocusNode();

  BusinessContactEntity supplier;

  List<String> menuList = ['Edit', 'Delete', 'See Bill'];

  //todo
  bool isDelivered = false;

  @override
  void dispose() {
    super.dispose();
    poNumberController.dispose();
    poNumberFocusNode.dispose();
    unitNumberController.dispose();
    unitNumberFocusNode.dispose();
    postalCodeController.dispose();
    postalCodeFocusNode.dispose();
    notesController.dispose();
    notesFocusNode.dispose();
  }

  @override
  void initState() {
    usePurchasePrice = true;
    super.initState();
  }

  String deliveryAddress;

  void onSelectMenu(String value) {
    switch (value) {
      case 'Edit':
        onChangeEditMode();
        break;
      case 'Delete':
        onDelete();
        break;
      case 'Share':
        onShare();
        break;
      case 'Convert To Bill':
        onConvertToBill();
        break;
      case 'Make A Copy':
        onMakeACopy();
        break;
      case 'Mark As Delivered':
        onMarkAsDelivered();
        break;
      case 'See Bill':
        onSeeBill();
        break;
    }
  }

  void unFocus() {
    notesFocusNode.unfocus();
    postalCodeFocusNode.unfocus();
    unitNumberFocusNode.unfocus();
    poNumberFocusNode.unfocus();
  }

  void onSave() async {
    unFocus();

    if (supplier == null) {
      showToast('Please select Supplier Name');
      return;
    }

    if (poNumberController.text.length == 0) {
      showToast(S.current.please_enter_the_po_number);
      return;
    }

    if (date == null) {
      showToast(S.current.please_select_issue_date);
      return;
    }

    if (deliveryDate == null) {
      showToast(S.current.please_select_delivery_date);
      return;
    }

    if (deliveryAddress == null) {
      showToast(S.current.please_select_delivery_address);
      return;
    }

    // if (unitNumberController.text.length == 0) {
    //   showToast('Please enter unit number.');
    //   return;
    // }

    if (postalCodeController.text.length == 0) {
      showToast(S.current.please_enter_postal_code);
      return;
    }

    if (currency == null) {
      showToast(S.current.please_select_currency);
      return;
    }

    if (serviceAndProductList.length == 0) {
      showToast(S.current.please_add_item);
      return;
    }

    List item = serviceAndProductList
        .map((e) => {
              'type': e.product != null ? 'product' : 'service',
              'type_id': e.id,
              'name': e.name,
              'quantity': e.number,
              'price': e.price,
              'is_tax_inclusive': e.isInclusive ? 1 : 0,
              'tax_id': e.tax.id,
              'tax_amount': e.taxAmount,
              'discount_type': e.discountType,
              'discount_value': e.discountValue,
            })
        .toList();
    List<int> attachmentIds = await uploadFiles();
    if (attachmentIds == null) return;

    PurchaseOrderEntity orderEntity;
    if (isEdit == true) {
      await post(
          HttpPurchaseOrderService.updatePurchaseOrders(
            data.id,
            item: item,
            contactId: supplier.id,
            total: total,
            subtotal: subTotal,
            currency_code: currency.key,
            issued_at: DateTimeUtils.formatDateTime(date, 'yyyy-MM-dd'),
            delivery_at:
                DateTimeUtils.formatDateTime(deliveryDate, 'yyyy-MM-dd'),
            inclusive_tax_amount: includesGST,
            exclusive_tax_amount: gst,
            number: poNumberController.text,
            address_unit: unitNumberController.text,
            address_line_1: deliveryAddress,
            address_postal_code: postalCodeController.text,
            notes: notesController.text,
            attachment_ids: attachmentIds,
          ),
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        orderEntity = getEntityFromResult<PurchaseOrderEntity>(result);
      });
    } else {
      await post(
          HttpPurchaseOrderService.createPurchaseOrders(
            item: item,
            contactId: supplier.id,
            total: total,
            subtotal: subTotal,
            currency_code: currency.key,
            issued_at: DateTimeUtils.formatDateTime(date, 'yyyy-MM-dd'),
            delivery_at:
                DateTimeUtils.formatDateTime(deliveryDate, 'yyyy-MM-dd'),
            inclusive_tax_amount: includesGST,
            exclusive_tax_amount: gst,
            address_unit: unitNumberController.text,
            address_line_1: deliveryAddress,
            address_postal_code: postalCodeController.text,
            number: poNumberController.text,
            notes: notesController.text,
            attachment_ids: attachmentIds,
          ),
          autoHandler: AutoHandler.Toast, onSuccess: (result) {
        orderEntity = getEntityFromResult<PurchaseOrderEntity>(result);
      });
    }

    if (orderEntity != null) {
      updateDashboard();
      if (isSelectMode == true) {
        obtainContext?.call((context) {
          Navigator.of(context).pop(orderEntity);
        });
      } else {
        id = orderEntity.id;
        await post(editDataFuture,
            loadingTip: S.current.loading,
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          isEdit = false;
          data = transformData(
              getEntityFromResult<PurchaseOrderDetailEntity>(result), false);
          update();
        });
      }
    }
  }

  void onChangeSupplierName() async {
    unFocus();
    final result = await Routes.navigateTo(
        RouterGenProviders.businessContactsPage,
        params: {
          'select': supplier,
        });
    if (result != null) {
      supplier = result;
      update();
    }
  }

  void onChangeIssueDate() {
    unFocus();
    obtainContext?.call((context) async {
      date = await showCalendarBottomSheet(context, date);
      update();
    });
  }

  void onChangeDeliveryDate() {
    unFocus();
    obtainContext?.call((context) async {
      deliveryDate = await showCalendarBottomSheet(context, deliveryDate,
          startDate: date, withinFinancialYear: false);
      update();
    });
  }

  void onChangeCurrency() {
    unFocus();
    obtainContext?.call((context) async {
      currency = await showCurrencyBottomSheet(context, currency);
      update();
    });
  }

  @override
  Future get editDataFuture => HttpPurchaseOrderService.showPurchaseOrders(id);

  @override
  FutureOr<PurchaseOrderDetailEntity> transformData(
      PurchaseOrderDetailEntity d, bool isCache) {
    // todo
    // isDelivered =
    supplier = d.contact;
    poNumberController.text = d.number;
    date = DateTimeUtils.toDateTime(d.issuedAt);
    deliveryDate = DateTimeUtils.toDateTime(d.deliveryAt);
    currency = Config.settingData.currency.firstWhere(
        (element) => element.key == d.currencyCode,
        orElse: () => null);
    unitNumberController.text = d.addressUnit;
    postalCodeController.text = d.addressPostalCode;
    isDelivered = d.deliveredAt != null;
    deliveryAddress = d.addressLine1;
    serviceAndProductList =
        d.items.map((e) => e.toServiceAndProduct()).toList();
    notesController.text = d.notes;
    if (d.status != 'closed') {
      if (d.billId == null) {
        menuList = [
          'Edit',
          'Delete',
          'Share',
          'Convert To Bill',
          'Make A Copy',
        ];
        // if (d.deliveredAt == null) {
        //   menuList.add('Mark As Delivered');
        // }
      } else {
        menuList = ['Edit', 'Delete', 'Share', 'See Bill'];
      }
    } else {
      menuList = ['Edit', 'Delete', 'Share', 'See Bill'];
    }
    if (isDelivered) {
      menuList.remove('Edit');
    }
    handleDataUpdate(d);
    return super.transformData(d, isCache);
  }

  void onConvertToBill() async {
    if (isPlanExpired()) return;
    final result = await Routes.navigateTo(RouterGenProviders.newBillPage,
        params: {'convert': data, 'isSelectMode': true});
    if (result != null) {
      onTry();
      updateDashboard();
    }
  }

  void onMakeACopy() {
    isEdit = null;
    id = null;
    update();
    loadSuggestNumber();
  }

  void onDelete() {
    if (isPlanExpired()) return;
    obtainContext?.call((context) async {
      bool isSuccess = await showDeleteTransactionInfoBottomSheet(context);
      if (isSuccess == true) {
        isSuccess = false;
        await post(HttpPurchaseOrderService.deletePurchaseOrders(data.id),
            loadingTip: S.current.loading,
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess == true) {
          updateDashboard();
          await showDeleteSuccessTipBottomSheet(context);
          Navigator.of(context).pop({
            'delete': true,
          });
        }
      }
    });
  }

  void onSeeBill() async {
    await Routes.navigateTo(RouterGenProviders.newBillPage,
        params: {'data': TransactionEntity()..transactionableId = data.billId});
    onTry();
  }

  void initGenerate(QuotationDetailEntity generate) {
    if (generate == null) return;
    supplier = generate.contact;
    date = DateTimeUtils.toDateTime(generate.issuedAt);
    deliveryDate = DateTimeUtils.toDateTime(generate.dueAt);
    currency = Config.settingData.currency.firstWhere(
        (element) => element.key == generate.currencyCode,
        orElse: () => null);
    serviceAndProductList =
        generate.items.map((e) => e.toServiceAndProduct()).toList();
    notesController.text = generate.notes;
  }

  void onChangeDeliveryAddress() {
    obtainContext?.call((context) async {
      AddressEntity entity = await showAddressBottomSheet(context, null,
          (String searchData) async {
        List<AddressEntity> addressList;
        await post(HttpCommonService.searchAddress(searchData),
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          addressList = getEntityFromResult<List<AddressEntity>>(result);
        });
        if (addressList == null) {
          addressList = [];
        }
        return addressList;
      });
      if (entity != null) {
        deliveryAddress = entity.address;
        postalCodeController.text = entity.code;
        update();
      }
    });
  }

  @override
  List<OcrAttachmentEntity> get attachments => data?.attachments;

  @override
  String get type => 'purchase_order';

  @override
  TextEditingController get numberCtl => poNumberController;

  void onMarkAsDelivered() {
    if (isPlanExpired()) return;
    obtainContext?.call((context) async {
      bool isSuccess =
          await showMarkTheItemsAsDeliveredAsJudgeBottomSheet(context);
      if (isSuccess == true) {
        isSuccess = false;
        await post(HttpPurchaseOrderService.deliver(id),
            loadingTip: S.current.loading,
            autoHandler: AutoHandler.Toast, onSuccess: (result) {
          isSuccess = true;
        });

        if (isSuccess == true) {
          await showMarkedAsDeliveredSuccessTipBottomSheet(context);
          onTry();
        }
      }
    });
  }

  void updateDashboard() {
    try {
      Providers.get<PurchasesProvider>(RouterGenProviders.purchasesPage, null)
          ?.getChildProvider<PurchasesSummaryProvider>()
          ?.loadData();
    } catch (e) {}
    try {
      Providers.get<PurchasesProvider>(RouterGenProviders.purchasesPage, null)
          ?.getChildProvider<PurchasesPoProvider>()
          ?.loadData();
    } catch (e) {}
    try {
      Providers.get<PurchasesProvider>(RouterGenProviders.purchasesPage, null)
          ?.getChildProvider<PurchasesBillProvider>()
          ?.loadData();
    } catch (e) {}
  }
}
